package reports;

import gui.common.SizeUnits;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import model.entity.Item;
import model.entity.Product;
import model.entity.container.ProductContainer;
import model.entity.container.ProductGroup;
import model.entity.container.StorageUnit;
import model.management.ProductGroupManager;
import reports.builders.IBuilder;
import reports.visitor.IVisitor;

import common.Size;

//@formatter:off
public class NoticesReport extends Report
{
  private static final String                    TITLE  = "Notices Report";

  private IVisitor                               visitor;
  
  private Map<ProductGroup, Iterator<Product>> data
              = new HashMap<ProductGroup, Iterator<Product>>();
  private Map<ProductGroup, Set<Product>> notice
              = new HashMap<ProductGroup, Set<Product>>();
  
  private final int                              WEIGHT = 0;
  private final int                              VOLUME = 1;
  private final int                              COUNT  = 2;
//@formatter:on
  public NoticesReport()
  {
    super(TITLE);

    visitor = new NoticesReportVisitor();
  }

  @Override
  public void buildReport(IBuilder<?> builder)
  {
    builder.addTitle("Notices");
    builder.addSubTitle("3-Month Supply Warnings");
    String inconsistent = " that is inconsistent with the following products:\n";
    for (Map.Entry<ProductGroup, Set<Product>> entry : notice.entrySet())
    {
      ProductGroup productGroup = entry.getKey();
      Set<Product> products = entry.getValue();
      if (!products.isEmpty())
      {
        String noticeProducts = "";

        for (Product product : products)
        {
          noticeProducts += "- " + product.getDescription() + "(size:"
              + product.getSize().toString() + ")\n";
        }

        String groupStr = null;
        ProductContainer group = productGroup;
        do
        {
          groupStr = group.getName() + (groupStr == null ? "" : " => " + groupStr);
          group = ((ProductGroup) group).getContainer();
        }
        while (group instanceof ProductGroup);
        groupStr = group.getName() + " => " + groupStr;

        groupStr = String.format("Product Group %s has a 3-month supply (%s)", groupStr,
            productGroup.getThreeMonthSupply());

        builder.addParagraph(groupStr + inconsistent + noticeProducts);
      }
    }
  }

  @Override
  protected void visitData() throws Exception
  {
    ProductGroupManager.getInstance().preOrderAccept(visitor);
  }

  @Override
  protected void interpretData() throws Exception
  {
    for (Map.Entry<ProductGroup, Iterator<Product>> entry : data.entrySet())
    {
      ProductGroup productGroup = entry.getKey();
      Iterator<Product> products = entry.getValue();

      Size threeMonthSupply = productGroup.getThreeMonthSupply();
      int productGroupUnitKind = getUnitKind(threeMonthSupply.getUnit());
      notice.put(productGroup, new HashSet<Product>());
      while (products.hasNext())
      {
        Product product = products.next();
        int productUnitKind = getUnitKind(product.getSize().getUnit());
        if (productGroupUnitKind != productUnitKind)
        {
          notice.get(productGroup).add(product);
        }
      }
    }
  }

  private int getUnitKind(SizeUnits unit)
  {
    switch (unit)
    {
      case Pounds:
      case Ounces:
      case Grams:
      case Kilograms:
        return WEIGHT;
      case Gallons:
      case Quarts:
      case Pints:
      case FluidOunces:
      case Liters:
        return VOLUME;
      case Count:
        return COUNT;
    }
    return -1;
  }

  private class NoticesReportVisitor implements IVisitor
  {
    @Override
    public void visit(ProductGroup productgroup)
    {
      data.put(productgroup, productgroup.getProducts());
    }

    @Override
    public void visit(StorageUnit storageunit)
    {
      // do nothing
    }

    @Override
    public void visit(Item item)
    {
      // do nothing
    }

    @Override
    public void visit(Product product)
    {
      // do nothing
    }
  }
}
